# TODO: tail
# tail <file>: 获取日志文件最后 n 行
# tail -f <file>: 持续打印一个日志文件（文件大小会随着时间增长)

import os
import sys
import time

sys.argv[0]  # tail.py
# sys.argv[1]: option -f or file
# sys.argv[2]: file

if len(sys.argv) == 2:
    file = sys.argv[1]

    # open 是 Python 内置的函数，用于打开文件。file 是要打开的文件名，"rb" 是打开模式：r 表示读文件，b 表示以二进制模式读取。
    with open(file, "rb") as f:   # with ... as ... 是一种上下文管理语法，它的好处是文件使用完后会自动关闭，不用我们手动写
        #f.seek(offset,whence) offset 是偏移量，whence 是起始位置：0 表示从文件开头算，1 表示从当前指针位置算，2 表示从文件末尾算。
        size = f.seek(0, 2)  # size of the file
        # look ahead of 7 lines
        p = size
        # 定义变量 rows ，用来记录找到的换行符数量，后续通过它控制取最后几行
        rows = 0
        # 定义一个空列表 chars ，用来存储从文件中读取的字节内容
        chars = []
        while True:
            if rows > 7 or p <= 1:
                break

            f.seek(p - 1, 0)  # 移动到 p - 1的位置上，然后读 1 个字符
            c = f.read(1)    # 把读取到的内容赋值给 c
            if c == b"\n":   #b"\n" 是二进制形式的换行符
                rows += 1
            p -= 1    # 每循环一次p-1
            chars.append(c)  # 把读取到的字节 c 添加到 chars 列表中
    # b"".join(...) 是把字节列表拼接成一个完整的字节串，赋值给result
    result = b"".join(chars[::-1])  # 切片chars[::-1]把列表中的元素反转（因为之前是倒着从文件末尾往开头读的字节，反转后就恢复成正常的文件内容顺序）
    # result 是字节串，decode("utf8") 把字节串解码成字符串（假设文件内容是 UTF-8 编码 ）
    print(result.decode("utf8"))


# -------------持续监控文件新增内容----------------

#  python tail.py -f 文件名 这样的命令（sys.argv[0] 是脚本名，sys.argv[1] 是 -f ，sys.argv[2] 是文件名 ）
if len(sys.argv) == 3:   #判断命令行传入的参数数量是不是 3 个
    option = sys.argv[1]  # 把命令行中第二个参数（也就是可能的选项，比如 -f ）赋值给 option 变量。
    if sys.argv[1] == "-f":  #判断第二个参数是不是 -f ，如果是，说明要执行持续跟踪文件的逻辑
        option = sys.argv

        # 实现 -f 选项功能
        file = sys.argv[2]

        # 把可能出错的代码放在 try 块里，如果执行过程中出错，就会跳到 except 块里处理，避免程序直接崩溃。
        try:   # try...except异常处理机制
            with open(file, 'rb') as f:
                # 先输出文件现有内容的最后7行
                size = f.seek(0, 2)
                p = size
                rows = 0
                chars = []
                while True:
                    if rows > 7 or p <= 1:
                        break
                    f.seek(p - 1, 0)
                    c = f.read(1)  # 把读取到的内容赋值给 c
                    if c == b'\n':
                        rows += 1  #循环增量
                    p -= 1
                    chars.append(c)  # 把读取到的字节 c 添加到 chars 列表中
                result = b''.join(chars[::-1])
                print(result.decode('utf8'), end='')

                # 记录当前文件大小，用于监控新增内容
                current_size = size

                print(f"正在跟踪文件更新: {file} (按 Ctrl+C 停止)")
                try:
                    while True:
                        # 让程序暂停 0.5 秒，避免一直循环检查文件，太占用电脑资源，实现定时检查的效果
                        time.sleep(0.5)  # 检查间隔
                        try:
                            #  os.path.getsize是os 模块里的函数，传入文件名，返回文件的大小（字节数 ）
                            new_size = os.path.getsize(file)   # 赋值给 new_size ，用来和之前记录的 current_size 对比，判断文件有没有新增内容
                            if new_size > current_size:
                                # 文件有新增内容，读取并输出
                                f.seek(current_size)  # 把文件指针移动到之前记录的文件大小位置（也就是文件原来的末尾位置 ），准备读取新增的内容
                                new_content = f.read(new_size - current_size)  #计算出文件新增的字节数
                                print(new_content.decode('utf8', errors='replace'), end='')
                                current_size = new_size
                        except FileNotFoundError:
                            print(f"\n错误: 文件 '{file}' 被删除或移动")
                            break
                except KeyboardInterrupt:  # 外面的 except 用来捕获用户按 Ctrl + C 产生的中断信号。
                    print("\n已停止跟踪")
        except Exception as e:
            print(f"错误: 无法读取文件 - {e}")